package lldp

import (
	"context"
	"encoding/json"
	"errors"
	"io"
	"log/slog"
	"net"
	"os"

	// "net/http"
	"os/exec"
	"time"

	mlldp "gitee.com/rogax/kagent/pkg/lldp/lldpd"
	"gitee.com/rogax/kagent/pkg/utils"
)

type LLDPREQUEST struct {
	Socket string          `json:"socket"`
	Time   time.Time       `json:"duration"`
	Ctx    context.Context `json:"ctx"`
}

type CAPABILITY struct {
	Type    string `json:"type"`
	Enabled bool   `json:"enabled"`
}

type CHASSIS struct {
	ID         []map[string]string `json:"id"`
	Name       []map[string]string `json:"name"`
	Descr      []map[string]string `json:"descr"`
	MgmtIP     []map[string]string `json:"mgmt-ip"`
	Capability []CAPABILITY        `json:"capability"`
}

type PORT struct {
	ID    []map[string]string `json:"id"`
	Descr []map[string]string `json:"descr"`
	TTL   []map[string]string `json:"ttl"`
}

type VLAN struct {
	VLAN_ID string `json:"vlan-id"`
	Pvid    bool   `json:"pvid"`
}

type INF struct {
	Name        string        `json:"name"`
	Via         string        `json:"via"`
	Rid         string        `json:"rid"`
	Age         string        `json:"age"`
	Chassis     []CHASSIS     `json:"chassis"`
	Port        []PORT        `json:"port"`
	Vlan        []VLAN        `json:"vlan"`
	UnknownTlvs []interface{} `json:"unknown-tlvs"`
}

type LLDP struct {
	Interface []INF `json:"interface"`
}

type LLDPS struct {
	LLDP []LLDP `json:"lldp"`
}

var (
	log                 *slog.Logger             = slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{AddSource: true}))
	lldpDiscoveryResult *[]mlldp.DiscoveryResult = &[]mlldp.DiscoveryResult{}
)

func New(ctx context.Context) *LLDPREQUEST {
	return &LLDPREQUEST{
		Socket: "/var/run/lldpd/lldpd.socket",
		Time:   time.Now(),
		Ctx:    ctx,
	}
}

func (l *LLDPREQUEST) GetFromLLDPService() (string, error) {
	cmd := exec.Command("lldpctl", "-f", "json0", "-u", l.Socket)
	stdout, err := cmd.StdoutPipe()
	// Close the pipe when the function returns
	defer stdout.Close()
	if err != nil {
		log.Error("无法获取标准输出: ", err)
		return "", err
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		log.Error("无法获取标准错误: ", err)
		return "", err
	}

	err = cmd.Start()
	if err != nil {
		log.Error("命令<lldpctl>启动失败: ", err)
		return "", err
	}

	output, _ := io.ReadAll(stdout)
	errorOutput, _ := io.ReadAll(stderr)

	// log.Println("标准输出: ", string(output))
	if len(errorOutput) > 0 {
		log.Error("命令执行错误输出: ", string(errorOutput))
		return "", errors.New(string(errorOutput))
	}

	err = cmd.Wait()
	if err != nil {
		log.Error("命令执行失败: ", err)
		return "", err
	}

	data := LLDPS{}
	if err := json.Unmarshal(output, &data); err != nil {
		log.Error("JSON解析错误: ", err)
		return "", err
	}
	if r, err := json.Marshal(data); err != nil {
		log.Error("JSON编码错误: ", err)
		return "", err
	} else {
		// log.Println("JSON编码成功: ", string(r))
		return string(r), nil
	}
}

func (l *LLDPREQUEST) Get() (string, error) {
	return l.GetFromLLDPService()
}

// func (l *LLDPREQUEST) Get() (string, error) {
// 	b, err := json.Marshal(lldpDiscoveryResult)
// 	if err != nil {
// 		log.Error("无法将LLDP结果数据转换为JSON: ", err)
// 		return "", err
// 	}

// 	return string(b), nil
// }

func (l *LLDPREQUEST) Rund() {
	// Start lldpd daemon
	lldpd_server := NewLLDPDServer()
	lldpd_server.Run()
	log.Info("LLDP抓包服务启动成功.")

	LLDPClientd(l.Ctx)
	log.Info("LLDP抓包数据分析协程启动成功.")
}

func LLDPClientd(ctx context.Context) {
	interfaces, err := utils.GetInterfaces()
	if err != nil {
		log.Error("unable to get interfaces", "error", err)
		os.Exit(-1)
	}

	if len(interfaces) < 2 {
		log.Info("exiting, because not enough interfaces are up - we need at least two")
		return
	}
	log.Info("will start lldp on interfaces", "interfaces", interfaces)

	dc := make(chan mlldp.DiscoveryResult, 100)

	for _, iface := range interfaces {
		ifi, err := net.InterfaceByName(iface)
		if err != nil {
			log.Error("lldpd failed to find interface", ifi.Name, err)
			continue
		}
		c := mlldp.NewClient(ctx, *ifi)
		go c.Start(log, dc)
		log.Info("LLDP抓包客户端启动成功: ", interfaces)
	}

	go func(ctx context.Context) {
		for {
			select {
			case <-ctx.Done():
				log.Info("LLDP抓包数据分析协程退出.")
				return
			case result := <-dc:
				log.Info("LLDP抓包收到数据", "result", result)
				for _, r := range *lldpDiscoveryResult {
					// if r.SysName == result.SysName && r.SysDescription == result.SysDescription {
					if r.SysName == result.SysName {
						r = result
						continue
					}
				}
				*lldpDiscoveryResult = append(*lldpDiscoveryResult, result)
			}
		}
	}(ctx)

}
